package context.render.framebuffer;

import context.render.framebuffer.target.DepthTarget;
import context.render.framebuffer.target.TextureTarget;
import context.render.tiu.texture.Texture;
import context.util.Data;
import java.nio.IntBuffer;
import java.util.LinkedList;
import math.vec2i;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;

public class FrameBuffer
{
    
    
    public int mID;
    
    public vec2i mSize;
    
    private LinkedList<TextureTarget> mLTexTargs;
    
    private DepthTarget mDepthTarg;
    
    
    public FrameBuffer(vec2i aSize)
    {
        // Create an ID for this FrameBuffer:
        IntBuffer id = Data.get().reserveDataInt(1);
        GL30.glGenFramebuffers(id);
        this.mID = id.get(0);
        // Init the size:
        this.mSize = aSize.copy();
        // Init the list of target textures:
        this.mLTexTargs = new LinkedList<>();
        // Init the DepthTarget:
        this.mDepthTarg = null;
    }
    
    
    public void attachTexture()
    {
        TextureTarget targetTex = new TextureTarget(this.mSize);
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.mID);
        GL30.glFramebufferTexture2D(
            GL30.GL_FRAMEBUFFER, // Target of attachment
            GL30.GL_COLOR_ATTACHMENT0 + this.mLTexTargs.size(), // Type of Texture
            GL11.GL_TEXTURE_2D, // TIU Type
            targetTex.mID, // Texture to attach
            0 // Mipmap Level
        );
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
        this.mLTexTargs.add(targetTex);
    }
    
    public void attachDepth()
    {
        if (this.mDepthTarg != null)
        {
            System.err.println("FrameBuffer already has a depth target attached!");
            System.exit(1);
        }
        this.mDepthTarg = new DepthTarget(this.mSize);
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.mID);
        GL30.glFramebufferRenderbuffer(
            GL30.GL_FRAMEBUFFER, 
            GL30.GL_DEPTH_ATTACHMENT, 
            GL30.GL_RENDERBUFFER, 
            this.mDepthTarg.mID
        );
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
    }
    
    public void verify()
    {
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.mID);
        int status = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
        if (status != GL30.GL_FRAMEBUFFER_COMPLETE)
        {
            System.err.println("FrameBuffer failed to attach textures.");
            System.exit(1);
        }
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
    }
    
    public void onEnd()
    {
        if (this.mDepthTarg != null)
        {
            this.mDepthTarg.onEnd();
        }
        GL30.glDeleteFramebuffers(this.mID);
        this.mSize = null;
    }
    
    
    public IntBuffer getDrawBuffers()
    {
        int numTargets = this.mLTexTargs.size();
        IntBuffer drawBuffers = Data.get().reserveDataInt(numTargets);
        for (int i = 0; i < numTargets; i++)
        {
            drawBuffers.put(GL30.GL_COLOR_ATTACHMENT0 + i);
        }
        drawBuffers.flip();
        return drawBuffers;
    }
    
    
    public Texture texture(int aIndex)
    {
        return this.mLTexTargs.get(aIndex);
    }
    
}
